from argparse import Namespace
import imp
from threading import Lock
import json
import time
import flask
from flask_socketio import SocketIO, rooms, leave_room, join_room, emit
from pandas import NA
from redis.client import PubSub
from socketio import BaseManager

from flask_app import flask_app, redis
import flask_app.websocket.redis_pub_sub as redis_pub_sub
from flask_app.websocket.socket_config import NAMESPACE

sio = SocketIO(flask_app, cors_allowed_origins='*', async_mode="threading")

socketio_background_task_existed = False
socketio_background_task_lock = Lock()
num = 0


# 根据订阅得到的item，获取需要发送到的event，data和room
def deal_item(item):
    # type为subscribe 跳过
    if item['type'] == 'subscribe':
        return None, None, None
    event = item['channel']
    item_data = json.loads(item['data']) or {}
    room = "unit_"+item_data.get('unit', '')
    data = item_data.get('data', {})
    ts = int(time.time())
    x = time.localtime(ts)
    t = time.strftime('%Y-%m-%d %H:%M:%S', x)
    data['redis_recv_time'] = t
    data['redis_recv_ts'] = ts
    print("deal_item [{}] room:{} , event:{}".format(t,room,event))
    return event, data, room


def notice(sub: PubSub, socket_io: SocketIO, app):
    def is_format_unit_room(room:str) -> bool:
        if room.startswith('unit_'):
            return True
        else:
            return False
    for item in sub.listen():
        # 获取该数据需要发送到的room（机组）
        event, data, room = deal_item(item)
        # print("event data room",event,data,room)
        if not (event and data and room):
            # print("subscribe item message. continue")
            continue
        manager: BaseManager = socket_io.server.manager
        namespaces = manager.get_namespaces()
        # namespace 不存在
        if not (NAMESPACE in namespaces):
            break
        # 选择指定namespace中存在的room列表（机组）
        now_rooms = list(manager.rooms[NAMESPACE].keys())
        
        # 获取当前存在的活跃room
        existed_room = list(filter(is_format_unit_room,now_rooms))
        
        if len(existed_room) == 0:
            break
        if room in existed_room:
            print(f"notice client event:{event},room:{room}")
            # print("data is",data)
            manager.emit(event, data, namespace=NAMESPACE, to=room)
            # print("send end")
    print("[INFO] no client . terminal task")
    global socketio_background_task_existed, num
    with socketio_background_task_lock:
        socketio_background_task_existed = False
        num -= 1



# 断开连接时自动退出所有room
@sio.on('disconnect', namespace=NAMESPACE)
def on_disconnect():
    # print("client disconnect.")
    sid = flask.request.sid
    for room in rooms(sid, namespace=NAMESPACE):
        # print("room is",room)
        leave_room(room)


# 订阅时加入room
@sio.on('join', namespace=NAMESPACE)
def on_join_room(data):
    rooms = data.get('rooms', list())
    # traversal rooms and client join the room
    for room in rooms:
        print("join in room: unit_%s"%str(room))
        format_room = "unit_{}".format(str(room))
        join_room(format_room)
    ts = int(time.time())
    x = time.localtime(ts)
    t = time.strftime('%Y-%m-%d %H:%M:%S', x)
    
    sio.emit('join_room_result', {"result": "join in room!!", "ts": ts, "time": t}, namespace=NAMESPACE)
    # 加入时创建task发送数据，room为空时，结束task

    global socketio_background_task_existed, num
    print(f"socketio_background_task_existed,:{socketio_background_task_existed},num:{num}")
    with socketio_background_task_lock:
        if not socketio_background_task_existed:
            num += 1
            #sio.start_background_task(target=test, redis_pub=redis_pub_sub.redis_pub, sio=sio)
            sio.start_background_task(target=notice, sub=redis_pub_sub.redis_sub, socket_io=sio, app=flask_app)
            socketio_background_task_existed = True


# leave时，离开指定room
@sio.on('leave', namespace=NAMESPACE)
def on_leave_room(data):
    rooms = data.get('rooms', list())
    for room in rooms:
        leave_room(str(room))


def test(redis_pub, sio):
    # print("start publish data")
    for i in range(10):
        redis_pub.publish_realtime('unit1', {'id': 1 * i, "ts": time.time()})
        redis_pub.publish_predict('unit1', {'id': 2 * i, "ts": time.time()})
        redis_pub.publish_warning('unit1', {'id': 3 * i, "ts": time.time()})
        redis_pub.publish_realtime('unit2', {'id': 21 * i, "ts": time.time()})
        redis_pub.publish_predict('unit2', {'id': 22 * i, "ts": time.time()})
        redis_pub.publish_warning('unit2', {'id': 23 * i, "ts": time.time()})
        sio.sleep(5)
        # print("publish data")
